﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace SDK {
    public class MagDevice : IMagDevice {
        public MagDevice() : base() {
        }

        [Obsolete("Use MagDevice() instead", false)]
        public MagDevice(IntPtr wnd) : base(wnd) {
        }
    }

    public class IMagDevice {
        private const uint MAX_CHANNELINDEX = 511;
        private const uint INVALID_CHANNEL = uint.MaxValue;
        private const uint DEFAULT_FPA_WIDTH = 384;
        private const uint DEFAULT_FPA_HEIGHT = 288;
        private const int DEFAULT_BAR_WIDTH = 24; // aligned by 4

        private uint mChannelIndex = INVALID_CHANNEL;
        private uint mIpAddr;
        private bool mIsRecordingAvi;
        private bool mIsRecordingMGS;
        private bool mIsRecordingLocalAvi;
        private bool mIsRecordingLocalMgs;
        private bool mIsPlayingLocalMgs;

        private GroupSDK.CAMERA_INFO mCamInfo;
        private GroupSDK.CAMERA_INFO_EX mCamInfoEx;
        private GroupSDK.CAMERA_REGCONTENT mCamRegContent;

        private Bitmap mIrImage;
        private Bitmap mVisImage;
        private Bitmap mColorbarImage;

        // For avoid alloc repeat
        private GroupSDK.OUTPUT_PARAM mOutPara;

        public IMagDevice() {
        }

        public uint GetDevIPAddress() {
            return mIpAddr;
        }

        public bool IsLinked() {
            return GroupSDK.MAG_IsLinked(mChannelIndex);
        }

        public bool LinkCamera(uint ip, uint timeout) {
            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else { // channelIndex is valid after loadddt
                if (GroupSDK.MAG_IsLinked(mChannelIndex)) {
                    return true;
                } else if (GroupSDK.MAG_IsProcessingImage(mChannelIndex)) { // not linked but processing for loadmgs
                    return false;
                } else {
                    newChannel(ref mChannelIndex);
                }
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                return false;
            }

            bool ret = GroupSDK.MAG_LinkCamera(mChannelIndex, ip, timeout);
            if (ret) {
                mIpAddr = ip;
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));
                GroupSDK.MAG_ReadCameraRegContent(mChannelIndex, ref mCamRegContent, timeout, 0);
            } else {
                delChannel(ref mChannelIndex);
            }

            return ret;
        }

        public bool LinkCamera(string ip, uint timeout) {
            return LinkCamera(hostToNetworkOrder(ip), timeout);
        }

        public bool LinkCameraEx(uint ip, ushort cmdPort, ushort imgPort, string cloudUser,
            string cloudPwd, uint serialNumber, string cameraUser, string cameraPwd, uint timeout) {
            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else { // channelIndex is valid after loadddt
                if (GroupSDK.MAG_IsLinked(mChannelIndex)) {
                    return true;
                } else if (GroupSDK.MAG_IsProcessingImage(mChannelIndex)) { // not linked but processing for loadmgs
                    return false;
                } else {
                    newChannel(ref mChannelIndex);
                }
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                return false;
            }

            bool ret = GroupSDK.MAG_LinkCameraEx(mChannelIndex, ip, cmdPort, imgPort, cloudUser,
                cloudPwd, serialNumber, cameraUser, cameraPwd, timeout);
            if (ret) {
                mIpAddr = ip;
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));
                GroupSDK.MAG_ReadCameraRegContent(mChannelIndex, ref mCamRegContent, timeout, 0);
            } else {
                delChannel(ref mChannelIndex);
            }

            return ret;
        }

        public bool LinkCameraEx(string ip, ushort cmdPort, ushort imgPort, string cloudUser,
            string cloudPwd, uint serialNumber, string cameraUser, string cameraPassword, uint timeout) {
            return LinkCameraEx(hostToNetworkOrder(ip), cmdPort, imgPort, cloudUser, cloudPwd, 
                serialNumber, cameraUser, cameraPassword, timeout);
        }

        public void DisLinkCamera() {
            //remember to stop sd storage before dislink
            if (mIsRecordingMGS) {
                GroupSDK.MAG_SDCardStorage(mChannelIndex, GroupSDK.SDStorageFileType.SDFileMGS, 0);
            }

            if (mIsRecordingAvi) {
                GroupSDK.MAG_SDCardStorage(mChannelIndex, GroupSDK.SDStorageFileType.SDFileAVI, 0);
            }

            mIpAddr = 0;
            GroupSDK.MAG_DisLinkCamera(mChannelIndex);
            delChannel(ref mChannelIndex);
        }

        public GroupSDK.CAMERA_INFO GetCamInfo() {
            return mCamInfo;
        }
        public GroupSDK.CAMERA_INFO_EX GetCamInfoEx() {
            return mCamInfoEx;
        }

        public GroupSDK.CAMERA_REGCONTENT GetRegContent() {
            return mCamRegContent;
        }

        public void ConvertPos2XY(uint pos, ref uint x, ref uint y) {
            uint w = (uint)mCamInfo.intFPAWidth;

            if (w != 0) {
                y = pos / w;
                x = pos - y * w;
            }
        }

        public uint ConvertXY2Pos(uint x, uint y) {
            return y * (uint)mCamInfo.intFPAWidth + x;
        }

        public uint GetRecentHeartBeat() {
            return GroupSDK.MAG_GetRecentHeartBeat(mChannelIndex);
        }

        public bool SetReConnectCallBack(GroupSDK.DelegateReconnect callback, IntPtr userData) {
            return GroupSDK.MAG_SetReConnectCallBack(mChannelIndex, callback, userData);
        }

        public bool ResetCamera() {
            if (mIsRecordingMGS) {
                GroupSDK.MAG_SDCardStorage(mChannelIndex, GroupSDK.SDStorageFileType.SDFileMGS, 0);
            }

            if (mIsRecordingAvi) {
                GroupSDK.MAG_SDCardStorage(mChannelIndex, GroupSDK.SDStorageFileType.SDFileAVI, 0);
            }

            return GroupSDK.MAG_ResetCamera(mChannelIndex);
        }

        public bool TriggerFFC() {
            return GroupSDK.MAG_TriggerFFC(mChannelIndex);
        }

        public bool SetIoAlarmState(bool bAlarm) {
            return GroupSDK.MAG_SetIoAlarmState(mChannelIndex, bAlarm);
        }

        public bool SetPTZCmd(GroupSDK.PTZIRCMD cmd, uint para) {
            return GroupSDK.MAG_SetPTZCmd(mChannelIndex, cmd, para);
        }

        public bool QueryPTZState(GroupSDK.PTZQuery query, ref int value, uint timeout) {
            return GroupSDK.MAG_QueryPTZState(mChannelIndex, query, ref value, timeout);
        }

        public bool SetSerialCmd(byte[] cmd, uint cmdLen) {
            return GroupSDK.MAG_SetSerialCmd(mChannelIndex, cmd, cmdLen);
        }

        public bool SetSerialCallBack(GroupSDK.DelegateSerial callback, IntPtr userData) {
            return GroupSDK.MAG_SetSerialCallBack(mChannelIndex, callback, userData);
        }

        public bool GetCameraTemperature(int[] t, uint timeout) {
            return GroupSDK.MAG_GetCameraTemperature(mChannelIndex, t, timeout);
        }

        public bool SetCameraRegContent(GroupSDK.CAMERA_REGCONTENT regContent) {
            return GroupSDK.MAG_SetCameraRegContent(mChannelIndex, ref regContent);
        }

        public bool SetIrregularROIs(GroupSDK.IRREGULAR_ROI[] rois, uint num) {
            return GroupSDK.MAG_SetIrregularROIs(mChannelIndex, rois, num);
        }

        public bool SetIrregularROIReportExCallBack(GroupSDK.DelegateIrregularROIReport callback, IntPtr userData) {
            return GroupSDK.MAG_SetIrregularROIReportExCallBack(mChannelIndex, callback, userData);
        }

        public bool SetObjRecoCallBack(GroupSDK.DelegateObjReco callback, IntPtr userData) {
            return GroupSDK.MAG_SetObjRecoCallBack(mChannelIndex, callback, userData);
        }

        public bool IsProcessingImage() {
            return GroupSDK.MAG_IsProcessingImage(mChannelIndex);
        }

        public bool StartProcessImage(GroupSDK.DelegateNewFrame newFrame, GroupSDK.STREAM_TYPE streamType, IntPtr userData) {
            generateOutParam((uint)mCamInfo.intFPAWidth, (uint)mCamInfo.intFPAHeight, (uint)mCamInfo.intVideoWidth,
                    (uint)mCamInfo.intVideoHeight, DEFAULT_BAR_WIDTH, (uint)mCamInfo.intVideoHeight);

            if (!newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, DEFAULT_BAR_WIDTH, mCamInfo.intVideoHeight)) {
                return false;
            }
            bool ret = GroupSDK.MAG_StartProcessImage(mChannelIndex, ref mOutPara, newFrame, (uint)streamType, userData);
            if (!ret) {
                destroyIrBitmap();
            }
            return ret;
        }

        public bool StartProcessPulseImage(GroupSDK.DelegateNewFrame newFrame, GroupSDK.STREAM_TYPE streamType, IntPtr userData) {
            generateOutParam((uint)mCamInfo.intFPAWidth, (uint)mCamInfo.intFPAHeight, (uint)mCamInfo.intVideoWidth,
                    (uint)mCamInfo.intVideoHeight, DEFAULT_BAR_WIDTH, (uint)mCamInfo.intVideoHeight);

            if (!newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, DEFAULT_BAR_WIDTH, mCamInfo.intVideoHeight)) {
                return false;
            }
            bool ret = GroupSDK.MAG_StartProcessPulseImage(mChannelIndex, ref mOutPara, newFrame, (uint)streamType, userData);
            if (!ret) {
                destroyIrBitmap();
            }
            return ret;
        }

        public bool TransferPulseImage() {
            return GroupSDK.MAG_TransferPulseImage(mChannelIndex);
        }

        public void StopProcessImage() {
            if (mIsRecordingLocalAvi) {
                LocalStorageAviStop();
            }

            if (mIsRecordingLocalMgs || IsLocalMgsPlaying()) {
                LocalStorageMgsStop();
            }

            GroupSDK.MAG_StopProcessImage(mChannelIndex);
            destroyIrBitmap();
        }

        public void SetColorPalette(GroupSDK.COLOR_PALETTE paletteIndex) {
            GroupSDK.MAG_SetColorPalette(mChannelIndex, paletteIndex);
        }

        public bool SetSubsectionEnlargePara(int x1, int x2, byte y1, byte y2) {
            return GroupSDK.MAG_SetSubsectionEnlargePara(mChannelIndex, x1, x2, y1, y2);
        }

        public void SetAutoEnlargePara(uint autoEnlargeRange, int brightOffset, int contrastOffset) {
            GroupSDK.MAG_SetAutoEnlargePara(mChannelIndex, autoEnlargeRange, brightOffset, contrastOffset);
        }

        public void SetIsothermalPara(int lowerLimit, int upperLimit) {
            GroupSDK.MAG_SetIsothermalPara(mChannelIndex, lowerLimit, upperLimit);
        }

        public void SetIsothermalParaEx(int lowerLimit, int upperLimit, byte r, byte g, byte b) {
            GroupSDK.MAG_SetIsothermalParaEx(mChannelIndex, lowerLimit, upperLimit, r, g, b);
        }

        public void SetEnhancedROI(uint enhancedRatio, uint x0, uint y0, uint x1, uint y1) {
            GroupSDK.MAG_SetEnhancedROI(mChannelIndex, enhancedRatio, x0, y0, x1, y1);
        }

        public bool GetApproximateGray2TemperatureLUT(int[] lut, uint size) {
            return GroupSDK.MAG_GetApproximateGray2TemperatureLUT(mChannelIndex, lut, size);
        }

        public void SetEXLevel(GroupSDK.EX ex, int centerX, int centerY) {
            GroupSDK.MAG_SetEXLevel(mChannelIndex, ex, centerX, centerY);
        }

        public GroupSDK.EX GetEXLevel() {
            return GroupSDK.MAG_GetEXLevel(mChannelIndex);
        }

        public void SetDetailEnhancement(int ddeLevel, int isQuickDDE) {
            GroupSDK.MAG_SetDetailEnhancement(mChannelIndex, ddeLevel, isQuickDDE);
        }

        public bool SetVideoContrast(int contrastOffset) {
            return GroupSDK.MAG_SetVideoContrast(mChannelIndex, contrastOffset);
        }

        public bool SetVideoBrightness(int brightnessOffset) {
            return GroupSDK.MAG_SetVideoBrightness(mChannelIndex, brightnessOffset);
        }

        public GroupSDK.FixSelection GetFixPara(ref GroupSDK.FIX_PARAM para) {
            return GroupSDK.MAG_GetFixPara(mChannelIndex, ref para);
        }

        public float SetFixPara(GroupSDK.FIX_PARAM para, GroupSDK.FixSelection fixSelection) {
            return GroupSDK.MAG_SetFixPara(mChannelIndex, ref para, fixSelection);
        }

        public int FixTemperature(int t, float emissivity, uint posX, uint posY) {
            return GroupSDK.MAG_FixTemperature(mChannelIndex, t, emissivity, posX, posY);
        }

        public IntPtr GetFilteredRaw() {
            return GroupSDK.MAG_GetFilteredRaw(mChannelIndex);
        }

        public bool GetFilteredRaw(ushort[] data) {
            return GroupSDK.MAG_GetFilteredRaw_copy(mChannelIndex, data, (uint)data.Length * sizeof(ushort));
        }

        public bool GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) {
            return GroupSDK.MAG_GetOutputBMPdataRGB24(mChannelIndex, data, (uint)data.Length, bOrderBGR);
        }

        public bool GetOutputBMPDataRGB24(IntPtr data, int size, bool bOrderBGR) {
            return GroupSDK.MAG_GetOutputBMPdataRGB24(mChannelIndex, data, (uint)size, bOrderBGR);
        }

        public bool GetOutputVideoDataRGB24(byte[] data, bool bOrderBGR) {
            return GroupSDK.MAG_GetOutputVideoDataRGB24(mChannelIndex, data, (uint)data.Length, bOrderBGR);
        }

        public bool GetOutputColorBarDataRGB24(byte[] data, bool bOrderBGR) {
            return GroupSDK.MAG_GetOutputColorBardataRGB24(mChannelIndex, data, (uint)data.Length, bOrderBGR);
        }

        /// <summary>
        /// Don't call it in loadDDT or loadBufferedDDT's callback function
        /// </summary>
        /// <returns></returns>
        public Bitmap GetOutputBitmapData() {
            if (mIrImage == null) {
                return null;
            }

            Rectangle rect = new Rectangle(0, 0, mIrImage.Width, mIrImage.Height);
            BitmapData bmpData = mIrImage.LockBits(rect, ImageLockMode.ReadWrite, mIrImage.PixelFormat);
            if (bmpData == null) {
                return null;
            }

            bool ret = GroupSDK.MAG_GetOutputBMPdataRGB24(mChannelIndex, bmpData.Scan0, 
                (uint)(mIrImage.Width* mIrImage.Height*3), true);
            if (!ret) {
                mIrImage.UnlockBits(bmpData);
                return null;
            }

            mIrImage.UnlockBits(bmpData);
            mIrImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return mIrImage;
        }

        public Bitmap GetOutputVideoBitmapData() {
            if (mIrImage == null) {
                return null;
            }

            Rectangle rect = new Rectangle(0, 0, mIrImage.Width, mIrImage.Height);
            BitmapData bmpData = mIrImage.LockBits(rect, ImageLockMode.ReadWrite, mIrImage.PixelFormat);
            if (bmpData == null) {
                return null;
            }

            bool ret = GroupSDK.MAG_GetOutputVideoDataRGB24(mChannelIndex, bmpData.Scan0,
                (uint)(mIrImage.Width * mIrImage.Height * 3), true);
            if (!ret) {
                mIrImage.UnlockBits(bmpData);
                return null;
            }

            mIrImage.UnlockBits(bmpData);
            mIrImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return mIrImage;
        }

        public Bitmap GetOutputColorbarBitmapData() {
            if (mColorbarImage == null) {
                IntPtr data = IntPtr.Zero;
                IntPtr info = IntPtr.Zero;
                if (!GetOutputColorBardata(ref data, ref info)) {
                    return null;
                }

                int[] buf = new int[3];
                Marshal.Copy(info, buf, 0, buf.Length);
                if (!newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, buf[1], buf[2])) {
                    return null;
                }
            }

            // copy data to bitmap
            Rectangle rect = new Rectangle(0, 0, mColorbarImage.Width, mColorbarImage.Height);
            BitmapData bmpData = mColorbarImage.LockBits(rect, ImageLockMode.ReadWrite, mColorbarImage.PixelFormat);
            if (bmpData == null) {
                return null;
            }

            bool ret = GroupSDK.MAG_GetOutputColorBardataRGB24(mChannelIndex, bmpData.Scan0,
                (uint)(mColorbarImage.Width * mColorbarImage.Height * 3), true);
            if (!ret) {
                mColorbarImage.UnlockBits(bmpData);
                return null;
            }

            mColorbarImage.UnlockBits(bmpData);
            mColorbarImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return mColorbarImage;
        }

        public GroupSDK.TEMP_STATE GetFrameStatisticalData() {
            IntPtr ptr = GroupSDK.MAG_GetFrameStatisticalData(mChannelIndex);
            if (ptr == IntPtr.Zero) {
                return new GroupSDK.TEMP_STATE();
            }
            return (GroupSDK.TEMP_STATE)Marshal.PtrToStructure(ptr, typeof(GroupSDK.TEMP_STATE));
        }

        public bool GetTemperatureData(int[] data, int isEnableExtCorrect) {
            return GroupSDK.MAG_GetTemperatureData(mChannelIndex, data, (uint)data.Length * 4, isEnableExtCorrect);
        }

        public bool GetTemperatureDataRaw(int[] data, int isEnableExtCorrect) {
            return GroupSDK.MAG_GetTemperatureData_Raw(mChannelIndex, data, (uint)data.Length * 4, isEnableExtCorrect);
        }

        public int GetTemperatureProbe(uint posX, uint posY, uint size) {
            return GroupSDK.MAG_GetTemperatureProbe(mChannelIndex, posX, posY, size);
        }

        public int GetLineTemperatureInfo(uint x0, uint y0, uint x1, uint y1, int[] info) {
            return GroupSDK.MAG_GetLineTemperatureInfo2(mChannelIndex, x0, y0, x1, y1, info);
        }

        public bool GetRectTemperatureInfo(uint x0, uint y0, uint x1, uint y1, int[] info) {
            return GroupSDK.MAG_GetRectTemperatureInfo(mChannelIndex, x0, y0, x1, y1, info);
        }

        public bool GetEllipseTemperatureInfo(uint x0, uint y0, uint x1, uint y1, int[] info) {
            return GroupSDK.MAG_GetEllipseTemperatureInfo(mChannelIndex, x0, y0, x1, y1, info);
        }

        public bool GetRgnTemperatureInfo(uint[] pos, uint num, int[] info) {
            return GroupSDK.MAG_GetRgnTemperatureInfo(mChannelIndex, pos, num, info);
        }

        public bool UseTemperatureMask(int isUse) {
            return GroupSDK.MAG_UseTemperatureMask(mChannelIndex, isUse);
        }

        public bool IsUsingTemperatureMask() {
            return GroupSDK.MAG_IsUsingTemperatureMask(mChannelIndex);
        }

        public bool SaveBMP(uint index, string fileName) {
            return GroupSDK.MAG_SaveBMP(mChannelIndex, index, fileName);
        }

        public bool SaveDDT(string fileName) {
            return GroupSDK.MAG_SaveDDT(mChannelIndex, fileName);
        }

        public int SaveDDT2Buffer(byte[] buffer) {
            return GroupSDK.MAG_SaveDDT2Buffer(mChannelIndex, buffer, (uint)buffer.Length);
        }

        public bool LoadDDT(string fileName) {
            return LoadDDT(fileName, null, IntPtr.Zero);
        }

        public bool LoadDDT(string fileName, GroupSDK.DelegateNewFrame funcFrame, IntPtr userData) {
            if (GroupSDK.MAG_IsLinked(mChannelIndex) || GroupSDK.MAG_IsProcessingImage(mChannelIndex)) {
                return false;
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else {
                newChannel(ref mChannelIndex);
            }
            
            if (mChannelIndex == INVALID_CHANNEL) {
                return false;
            }

            generateOutParam(DEFAULT_FPA_WIDTH, DEFAULT_FPA_HEIGHT, DEFAULT_FPA_WIDTH,
                DEFAULT_FPA_HEIGHT, DEFAULT_BAR_WIDTH, DEFAULT_FPA_HEIGHT);
            bool ret = GroupSDK.MAG_LoadDDT(mChannelIndex, ref mOutPara, fileName, funcFrame, userData);
            if (ret) {
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));

                newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, 
                    (int)mOutPara.intColorbarWidth, (int)mOutPara.intColorbarHeight);
            } else {
                delChannel(ref mChannelIndex);
            }

            return ret;
        }

        public bool LoadBufferedDDT(byte[] data) {
            return LoadBufferedDDT(data, data.Length, null, IntPtr.Zero);
        }

        public bool LoadBufferedDDT(byte[] data, int size) {
            return LoadBufferedDDT(data, size, null, IntPtr.Zero);
        }

        public bool LoadBufferedDDT(byte[] data, int size, GroupSDK.DelegateNewFrame funcFrame, IntPtr userData) {
            if (GroupSDK.MAG_IsLinked(mChannelIndex) || GroupSDK.MAG_IsProcessingImage(mChannelIndex)) {
                return false;
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else {
                newChannel(ref mChannelIndex);
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                return false;
            }

            generateOutParam(DEFAULT_FPA_WIDTH, DEFAULT_FPA_HEIGHT, DEFAULT_FPA_WIDTH,
                DEFAULT_FPA_HEIGHT, DEFAULT_BAR_WIDTH, DEFAULT_FPA_HEIGHT);
            bool ret = GroupSDK.MAG_LoadBufferedDDT(mChannelIndex, ref mOutPara, data, (uint)size, funcFrame, userData);
            if (ret) {
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));

                newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, 
                    (int)mOutPara.intColorbarWidth, (int)mOutPara.intColorbarHeight);
            } else {
                delChannel(ref mChannelIndex);
            }

            return ret;
        }

        public bool LocalStorageAviStart(string fileName, uint samplePeriod) {
            mIsRecordingLocalAvi |= GroupSDK.MAG_LocalStorageAviStart(mChannelIndex, fileName, samplePeriod);
            return mIsRecordingLocalAvi;
        }

        public void LocalStorageAviStop() {
            GroupSDK.MAG_LocalStorageAviStop(mChannelIndex);
            mIsRecordingLocalAvi = false;
        }

        public bool IsLocalAviRecording() {
            return mIsRecordingLocalAvi;
        }

        public bool LocalStorageMgsRecord(string fileName, uint samplePeriod) {
            mIsRecordingLocalMgs |= GroupSDK.MAG_LocalStorageMgsRecord(mChannelIndex, fileName, samplePeriod);
            return mIsRecordingLocalMgs;
        }

        public int LocalStorageMgsPlay(string fileName, GroupSDK.DelegateNewFrame funcFrame, IntPtr userData) {
            if (GroupSDK.MAG_IsLinked(mChannelIndex) || GroupSDK.MAG_IsProcessingImage(mChannelIndex)) {
                return 0;
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else {
                newChannel(ref mChannelIndex);
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                return 0;
            }

            int totalFrames = GroupSDK.MAG_LocalStorageMgsPlay(mChannelIndex, fileName, funcFrame, userData);
            if (totalFrames > 0) {
                mIsPlayingLocalMgs = true;
            }

            if (mIsPlayingLocalMgs) {
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));

                newIrBitmapIfNeeded(mCamInfo.intVideoWidth,  mCamInfo.intVideoHeight, 0, 0); // MGS bar size cannot be got here
            } else {
                delChannel(ref mChannelIndex);
            }

            return totalFrames;
        }

        public bool LocalStorageMgsSeekFrame(uint hFrame) {
            return GroupSDK.MAG_LocalStorageMgsSeekFrame(mChannelIndex, hFrame);
        }

        public bool LocalStorageMgsPopFrame() {
            return GroupSDK.MAG_LocalStorageMgsPopFrame(mChannelIndex);
        }

        public void LocalStorageMgsStop() {
            GroupSDK.MAG_LocalStorageMgsStop(mChannelIndex);
            if (!GroupSDK.MAG_IsProcessingImage(mChannelIndex)) {
                delChannel(ref mChannelIndex);
                destroyIrBitmap();
            }
            mIsRecordingLocalMgs = false;
            mIsPlayingLocalMgs = false;
        }

        public bool IsLocalMgsRecording() {
            return mIsRecordingLocalMgs;
        }

        public bool IsLocalMgsPlaying() {
            return mIsPlayingLocalMgs;
        }

        public bool SDCardStorage(GroupSDK.SDStorageFileType filetype, uint para) {
            bool ret = GroupSDK.MAG_SDCardStorage(mChannelIndex, filetype, para);

            if (ret && filetype == GroupSDK.SDStorageFileType.SDFileMGS) {
                if (para == 1) {
                    mIsRecordingMGS = true;
                } else {
                    mIsRecordingMGS = false;
                }
            }

            if (ret && filetype == GroupSDK.SDStorageFileType.SDFileAVI) {
                if (para == 1) {
                    mIsRecordingAvi = true;
                } else {
                    mIsRecordingAvi = false;
                }
            }

            return ret;
        }

        public void Lock() {
            GroupSDK.MAG_LockFrame(mChannelIndex);
        }

        public void Unlock() {
            GroupSDK.MAG_UnLockFrame(mChannelIndex);
        }

        public int EstimateUnderArmTempFromForeheadRect(uint x0, uint y0, uint x1, uint y1) {
            return GroupSDK.MAG_EstimateUnderArmTempFromForeheadRect(mChannelIndex, x0, y0, x1, y1);
        }

        public bool ConvertIrCorr2VisCorr(int irX, int irY, float distance, ref int visX, ref int visY) {
            return GroupSDK.MAG_ConvertIrCorr2VisCorr(mChannelIndex, irX, irY, distance, ref visX, ref visY);
        }

        public bool ConvertVisCorr2IrCorr(int visX, int visY, float distance, ref int irX, ref int irY) {
            return GroupSDK.MAG_ConvertVisCorr2IrCorr(mChannelIndex, visX, visY, distance, ref irX, ref irY);
        }

        public bool SaveFIR(string fileName) {
            return GroupSDK.MAG_SaveFIR(mChannelIndex, fileName);
        }

        public bool LoadFIR(GroupSDK.OUTPUT_PARAM param, string fileName, GroupSDK.DelegateNewFrame funcFrame, IntPtr userData) {
            if (GroupSDK.MAG_IsLinked(mChannelIndex) || GroupSDK.MAG_IsProcessingImage(mChannelIndex)) {
                return false;
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else {
                newChannel(ref mChannelIndex);
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                return false;
            }

            generateOutParam(DEFAULT_FPA_WIDTH, DEFAULT_FPA_HEIGHT, DEFAULT_FPA_WIDTH,
                DEFAULT_FPA_HEIGHT, DEFAULT_BAR_WIDTH, DEFAULT_FPA_HEIGHT);
            bool ret = GroupSDK.MAG_LoadFIR(mChannelIndex, ref mOutPara, fileName, funcFrame, userData);
            if (ret) {
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));

                newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, 
                    (int)mOutPara.intColorbarWidth, (int)mOutPara.intColorbarHeight);
            } else {
                delChannel(ref mChannelIndex);
            }

            return ret;
        }

        public bool NpuSetNetwork(GroupSDK.NPU_SRC srcPara, string networkFile, uint timeout) {
            return GroupSDK.MAG_NPU_SetNetwork(mChannelIndex, ref srcPara, networkFile, timeout);
        }

        public bool NpuSetWeight(string weightFile, uint timeout) {
            return GroupSDK.MAG_NPU_SetWeight(mChannelIndex, weightFile, timeout);
        }

        public bool NpuUpdateTestImage(string testFile, uint timeout) {
            return GroupSDK.MAG_NPU_UpdateTestImage(mChannelIndex, testFile, timeout);
        }

        public void NpuStop() {
            GroupSDK.MAG_NPU_Stop(mChannelIndex);
        }

        public bool NpuStart(uint intInferencePriod, GroupSDK.DelegateNpu funcNPU, IntPtr userData) {
            return GroupSDK.MAG_NPU_Start(mChannelIndex, intInferencePriod, funcNPU, userData);
        }

        //////////////////////////////////VISIBLE////////////////////////////////////////

        public bool VisSetInitCallback(GroupSDK.DelegateVisInitialize callback, IntPtr userData) {
            return GroupSDK.MAG_SetVisInitCallback(mChannelIndex, callback, userData);
        }

        public int VisPlay(string rtspUrl, GroupSDK.VideoPixFormat pixFormat, 
            GroupSDK.DelegateVisNewFrame callback, IntPtr userData, int protocol, int timeout) {
            int ret = GroupSDK.MAG_StartVis(mChannelIndex, rtspUrl, pixFormat, callback, userData, protocol, timeout);
            if (ret == 0 && mVisImage == null) {
                mVisImage = new Bitmap(VisGetWidth(), VisGetHeight(), PixelFormat.Format32bppRgb);
            }
            return ret;
        }

        public int VisGetWidth() {
            return GroupSDK.MAG_VisWidth(mChannelIndex);
        }

        public int VisGetHeight() {
            return GroupSDK.MAG_VisHeight(mChannelIndex);
        }

        /// <summary>
        /// Must be called before dislink()
        /// </summary>
        public void VisStop() {
            GroupSDK.MAG_StopVis(mChannelIndex);
            if (mVisImage != null) {
                mVisImage.Dispose();
                mVisImage = null;
            }
        }

        public bool VisIsPlaying() {
            int ret = GroupSDK.MAG_IsVisStarted(mChannelIndex);
            return ret == 1 ? true : false;
        }

        public int VisSaveBMP(string fileName) {
            return GroupSDK.MAG_SaveVis(mChannelIndex, fileName);
        }

        public void VisSetReconnectCallback(GroupSDK.DelegateVisReconnect callback, IntPtr userData) {
            GroupSDK.MAG_SetVisReconnectCallback(mChannelIndex, callback, userData);
        }

        public void VisLock() {
            GroupSDK.MAG_LockVisFrame(mChannelIndex);
        }

        public void VisUnlock() {
            GroupSDK.MAG_UnlockVisFrame(mChannelIndex);
        }

        public int VisGetData(ref IntPtr data, ref IntPtr info) {
            return GroupSDK.MAG_GetVisData(mChannelIndex, ref data, ref info);
        }

        public Bitmap VisGetBitmap() {
            if (mVisImage == null) {
                return null;
            }

            Rectangle rect = new Rectangle(0, 0, mVisImage.Width, mVisImage.Height);
            BitmapData bmpData = mVisImage.LockBits(rect, ImageLockMode.ReadWrite, mVisImage.PixelFormat);
            if (bmpData == null) {
                return null;
            }

            int ret = GroupSDK.MAG_GetVisData_copy(mChannelIndex, bmpData.Scan0, mVisImage.Width * mVisImage.Height * 4);
            if (ret < 0) {
                mVisImage.UnlockBits(bmpData);
                return null;
            }

            mVisImage.UnlockBits(bmpData);
            return mVisImage;
        }

        private uint newChannel() {
            uint channelIndex = INVALID_CHANNEL;

            for (uint i = 1; i <= MAX_CHANNELINDEX; i++) {
                if (GroupSDK.MAG_IsChannelAvailable(i)) { //find an unused channel
                    continue;
                }

                if (!GroupSDK.MAG_NewChannel(i)) {
                    return INVALID_CHANNEL;
                }

                channelIndex = i;
                break;
            }

            if (channelIndex == INVALID_CHANNEL) { // all channel has been used
                return INVALID_CHANNEL;
            }

            if (!GroupSDK.MAG_Initialize(channelIndex, IntPtr.Zero)) {
                GroupSDK.MAG_DelChannel(channelIndex);
                return INVALID_CHANNEL;
            }

            return channelIndex;
        }

        private bool newChannel(ref uint channelIndex) {
            if (!GroupSDK.MAG_IsChannelAvailable(channelIndex)) {
                if (!GroupSDK.MAG_NewChannel(channelIndex)) {
                    channelIndex = INVALID_CHANNEL;
                    return false;
                }
            }

            if (!GroupSDK.MAG_IsInitialized(channelIndex)) {
                if (!GroupSDK.MAG_Initialize(channelIndex, IntPtr.Zero)) {
                    GroupSDK.MAG_DelChannel(channelIndex);
                    channelIndex = INVALID_CHANNEL;
                    return false;
                }
            }

            return true;
        }

        private void delChannel(ref uint channelIndex) {
            if (GroupSDK.MAG_IsInitialized(channelIndex)) {
                GroupSDK.MAG_Free(channelIndex);
            }
            if (GroupSDK.MAG_IsChannelAvailable(channelIndex)) {
                GroupSDK.MAG_DelChannel(channelIndex);
            }
            channelIndex = INVALID_CHANNEL;
        }

        private uint hostToNetworkOrder(string ip) {
            string[] parts = ip.Split('.');
            if (parts.Length != 4) { // ipv4 supported only
                return uint.MaxValue;
            }

            return uint.Parse(parts[0]) | (uint.Parse(parts[1]) << 8) | (uint.Parse(parts[2]) << 16) | (uint.Parse(parts[3]) << 24);
        }

        private GroupSDK.OUTPUT_PARAM generateOutParam(uint fpaWidth, uint fpaHeight, 
            uint bmpWidth, uint bmpHeight, uint barWidth, uint barHeight) {
            mOutPara = new GroupSDK.OUTPUT_PARAM();
            mOutPara.intFPAWidth = fpaWidth;
            mOutPara.intFPAHeight = fpaHeight;
            mOutPara.intBMPWidth = bmpWidth;
            mOutPara.intBMPHeight = bmpHeight;
            mOutPara.intColorbarWidth = (uint)(barWidth & ~3);
            mOutPara.intColorbarHeight = barHeight;
            return mOutPara;
        }

        private bool newIrBitmapIfNeeded(int bmpWidth, int bmpHeight, int barWidth, int barHeight) {
            if (mIrImage == null ||
                    (mIrImage != null && (mIrImage.Width != bmpWidth || mIrImage.Height != bmpHeight))) {
                if (mIrImage != null) {
                    mIrImage.Dispose();
                    mIrImage = null;
                }
                if (bmpWidth > 0 && bmpWidth <= 8192 && bmpHeight > 0 && bmpHeight <= 8192) {
                    mIrImage = new Bitmap(bmpWidth, bmpHeight, PixelFormat.Format24bppRgb);
                }
            }

            barWidth &= ~3;
            if (mColorbarImage == null ||
                (mColorbarImage != null && (mColorbarImage.Width != barWidth || mColorbarImage.Height != barHeight))) {
                if (mColorbarImage != null) {
                    mColorbarImage.Dispose();
                    mColorbarImage = null;
                }
                if (barWidth > 0 && barHeight > 0) {
                    mColorbarImage = new Bitmap(barWidth, barHeight, PixelFormat.Format24bppRgb);
                }
            }

            return true;
        }

        private void destroyIrBitmap() {
            if (mIrImage != null) {
                mIrImage.Dispose();
                mIrImage = null;
            }
            if (mColorbarImage != null) {
                mColorbarImage.Dispose();
                mColorbarImage = null;
            }
        }

        #region Deprecated apis, will be deleted later!!

        [Obsolete("Use MagDevice() instead", false)]
        public IMagDevice(IntPtr wnd) {
        }

        [Obsolete("Not needed", false)]
        public bool Initialize() {
            return true;
        }

        [Obsolete("Not needed", false)]
        public void DeInitialize() {
        }

        [Obsolete("Not needed", false)]
        public bool IsInitialized() {
            return true;
        }

        [Obsolete("Use StartProcessImage(GroupSDK.DelegateNewFrame, GroupSDK.STREAM_TYPE, IntPtr) instead", false)]
        public bool StartProcessImage(GroupSDK.OUTPUT_PARAM para, GroupSDK.DelegateNewFrame newFrame,
            uint streamType, IntPtr userData) {
            return StartProcessImage(newFrame, (GroupSDK.STREAM_TYPE)streamType, userData);
        }

        [Obsolete("Use StartProcessPulseImage(GroupSDK.DelegateNewFrame, GroupSDK.STREAM_TYPE, IntPtr) instead", false)]
        public bool StartProcessPulseImage(GroupSDK.OUTPUT_PARAM para, GroupSDK.DelegateNewFrame newFrame,
            uint streamType, IntPtr userData) {
            return StartProcessPulseImage(newFrame, (GroupSDK.STREAM_TYPE)streamType, userData);
        }

        [Obsolete("Use SetPTZCmd(GroupSDK.PTZIRCMD cmd, uint para) instead", false)]
        public bool AutoFocus() {
            return GroupSDK.MAG_ExecAutoFocus(mChannelIndex);
        }

        [Obsolete("Use SetFixPara(GroupSDK.FIX_PARAM para, GroupSDK.FixSelection fixSection) instead", false)]
        public float SetFixPara(ref GroupSDK.FIX_PARAM para, bool isEnableCameraCorrect) {
            return GroupSDK.MAG_SetFixPara(mChannelIndex, ref para, isEnableCameraCorrect ?
                GroupSDK.FixSelection.FixSelectionSceneAverageReflect : GroupSDK.FixSelection.FixSelectionDisabled);
        }

        [Obsolete("Use SetFixPara(GroupSDK.FIX_PARAM para, GroupSDK.FixSelection fixSection) instead", false)]
        public float SetFixPara(GroupSDK.FIX_PARAM para, bool isEnableCameraCorrect) {
            return GroupSDK.MAG_SetFixPara(mChannelIndex, ref para, isEnableCameraCorrect ?
                GroupSDK.FixSelection.FixSelectionSceneAverageReflect : GroupSDK.FixSelection.FixSelectionDisabled);
        }

        [Obsolete("Use GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) or GetOutputBitmapData() instead", false)]
        public bool GetOutputBMPdata(ref IntPtr data, ref IntPtr info) {
            return GroupSDK.MAG_GetOutputBMPdata(mChannelIndex, ref data, ref info);
        }

        [Obsolete("Use GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) or GetOutputBitmapData() instead", false)]
        public bool GetOutputBMPdata_copy(byte[] data, uint size) {
            return GroupSDK.MAG_GetOutputBMPdata_copy(mChannelIndex, data, size);
        }

        [Obsolete("Use GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) or GetOutputBitmapData() instead", false)]
        public bool GetOutputBMPdata_copy(IntPtr data, uint size) {
            return GroupSDK.MAG_GetOutputBMPdata_copy(mChannelIndex, data, size);
        }

        [Obsolete("Use GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) or GetOutputBitmapData() instead", false)]
        public bool GetOutputBMPdata_copy(byte[] data) {
            return GroupSDK.MAG_GetOutputBMPdata_copy(mChannelIndex, data, (uint)data.Length);
        }

        [Obsolete("Use GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) or GetOutputBitmapData() instead", false)]
        public bool GetOutputBMPdataRGB24(IntPtr data, uint size, bool bOrderBGR) {
            return GroupSDK.MAG_GetOutputBMPdataRGB24(mChannelIndex, data, size, bOrderBGR);
        }

        [Obsolete("Use GetOutputBMPDataRGB24(byte[] data, bool bOrderBGR) or GetOutputBitmapData() instead", false)]
        public bool GetOutputBMPdataRGB24(byte[] data, uint size, bool bOrderBGR) {
            return GroupSDK.MAG_GetOutputBMPdataRGB24(mChannelIndex, data, size, bOrderBGR);
        }

        [Obsolete("Use GetOutputColorBarDataRGB24(byte[] data, bool bOrderBGR) or GetOutputColorbarBitmapData() instead", false)]
        public bool GetOutputColorBardata(ref IntPtr data, ref IntPtr info) {
            return GroupSDK.MAG_GetOutputColorBardata(mChannelIndex, ref data, ref info);
        }

        [Obsolete("Use GetOutputColorBarDataRGB24(byte[] data, bool bOrderBGR) or GetOutputColorbarBitmapData() instead", false)]
        public bool GetOutputColorBardata_copy(IntPtr data, uint size) {
            return GroupSDK.MAG_GetOutputColorBardata_copy(mChannelIndex, data, size);
        }

        [Obsolete("Use GetOutputColorBarDataRGB24(byte[] data, bool bOrderBGR) or GetOutputColorbarBitmapData() instead", false)]
        public bool GetOutputColorBardata_copy(byte[] data, uint size) {
            return GroupSDK.MAG_GetOutputColorBardata_copy(mChannelIndex, data, size);
        }

        [Obsolete("Use GetOutputColorBarDataRGB24(byte[] data, bool bOrderBGR) or GetOutputColorbarBitmapData() instead", false)]
        public bool GetOutputColorBardata_copy(byte[] data) {
            return GroupSDK.MAG_GetOutputColorBardata_copy(mChannelIndex, data, (uint)data.Length);
        }

        [Obsolete("Use GetOutputVideoDataRGB24(byte[] data, bool bOrderBGR) or GetOutputVideoBitmapData() instead", false)]
        public bool GetOutputVideoData(ref IntPtr data, ref IntPtr info) {
            return GroupSDK.MAG_GetOutputVideoData(mChannelIndex, ref data, ref info);
        }

        [Obsolete("Use GetOutputVideoDataRGB24(byte[] data, bool bOrderBGR) or GetOutputVideoBitmapData() instead", false)]
        public bool GetOutputVideoData_copy(IntPtr data, uint size) {
            return GroupSDK.MAG_GetOutputVideoData_copy(mChannelIndex, data, size);
        }

        [Obsolete("Use GetOutputVideoDataRGB24(byte[] data, bool bOrderBGR) or GetOutputVideoBitmapData() instead", false)]
        public bool GetOutputVideoData_copy(byte[] data) {
            return GroupSDK.MAG_GetOutputVideoData_copy(mChannelIndex, data, (uint)data.Length);
        }

        [Obsolete("Use GetTemperatureData(int[] data, int isEnableExtCorrect) instead", false)]
        public bool GetTemperatureData(int[] data, uint size, int isEnableExtCorrect) {
            return GroupSDK.MAG_GetTemperatureData(mChannelIndex, data, (uint)data.Length * 4, isEnableExtCorrect);
        }

        [Obsolete("Use GetTemperatureDataRaw(int[] data, int isEnableExtCorrect) instead", false)]
        public bool GetTemperatureDataRaw(int[] data, uint intBufferSize, int isEnableExtCorrect) {
            return GroupSDK.MAG_GetTemperatureData_Raw(mChannelIndex, data, (uint)data.Length * 4, isEnableExtCorrect);
        }

        [Obsolete("Use LoadDDT(string fileName) instead", false)]
        public bool LoadDDT(GroupSDK.OUTPUT_PARAM para, string fileName, GroupSDK.DelegateNewFrame funcFrame, IntPtr userData) {
            return LoadDDT(fileName, funcFrame, userData);
        }

        [Obsolete("Use LoadBufferedDDT(byte[] data) instead", false)]
        public bool LoadBufferedDDT(GroupSDK.OUTPUT_PARAM para, IntPtr data, uint size, 
            GroupSDK.DelegateNewFrame funcFrame, IntPtr userData) {
            if (GroupSDK.MAG_IsLinked(mChannelIndex) || GroupSDK.MAG_IsProcessingImage(mChannelIndex)) {
                return false;
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                mChannelIndex = newChannel();
            } else {
                newChannel(ref mChannelIndex);
            }

            if (mChannelIndex == INVALID_CHANNEL) {
                return false;
            }

            bool ret = GroupSDK.MAG_LoadBufferedDDT(mChannelIndex, ref para, data, size, funcFrame, userData);
            if (ret) {
                GroupSDK.MAG_GetCamInfo(mChannelIndex, ref mCamInfo, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO)));
                GroupSDK.MAG_GetCamInfoEx(mChannelIndex, ref mCamInfoEx, Marshal.SizeOf(typeof(GroupSDK.CAMERA_INFO_EX)));

                newIrBitmapIfNeeded(mCamInfo.intVideoWidth, mCamInfo.intVideoHeight, 
                    (int)para.intColorbarWidth, (int)para.intColorbarHeight);
            } else {
                delChannel(ref mChannelIndex);
            }

            return ret;
        }

        [Obsolete("Use SetIrregularROIs(GroupSDK.IRREGULAR_ROI[] rois, uint num) instead", false)]
        public bool SetUserROIs(GroupSDK.USER_ROI rois) {
            return GroupSDK.MAG_SetUserROIs(mChannelIndex, ref rois);
        }

        [Obsolete("Use SetIrregularROIs(GroupSDK.IRREGULAR_ROI[] rois, uint num) instead", false)]
        public bool SetUserROIsEx(GroupSDK.RECT_ROI[] rois, uint num) {
            return GroupSDK.MAG_SetUserROIsEx(mChannelIndex, rois, num);
        }

        [Obsolete("Use SetIrregularROIReportExCallBack(GroupSDK.DelegateIrregularROIReport callback, IntPtr userData) instead", false)]
        public bool SetROIReportCallBack(GroupSDK.DelegateROIReport callback, IntPtr userData) {
            return GroupSDK.MAG_SetROIReportCallBack(mChannelIndex, callback, userData);
        }

        [Obsolete("Use SetIrregularROIReportExCallBack(GroupSDK.DelegateIrregularROIReport callback, IntPtr userData) instead", false)]
        public bool SetIrregularROIReportCallBack(GroupSDK.DelegateROIReport callback, IntPtr userData) {
            return GroupSDK.MAG_SetIrregularROIReportCallBack(mChannelIndex, callback, userData);
        }

        [Obsolete("Use SetObjRecoCallBack(GroupSDK.DelegateObjReco callback, IntPtr userData) instead", false)]
        public bool SetObjRecCallBack(GroupSDK.DelegateObjRec callback, IntPtr userData) {
            return GroupSDK.MAG_SetObjRecoCallBack(mChannelIndex, callback, userData);
        }
        #endregion
    }
}
